package com.meshnetics.gb.type.impl.zcl.example;


import com.meshnetics.gb.type.CustomFormatter;
import com.meshnetics.gb.type.DataType;
import com.meshnetics.gb.type.DataValue;
import com.meshnetics.gb.type.impl.StringUtils;
import com.meshnetics.gb.type.impl.zcl.GeneralData;
import com.meshnetics.gb.type.impl.zcl.TypeGeneralData;

import java.util.Map;

/**
 * WRITE DESCRIPTION HERE
 *
 * @author AKovylin
 */
public class GeneralDataCustomFormatter implements CustomFormatter {
    public static final String PARAMETER_GROUP_LENGTH = "GroupLength";
    public static final int DEFAULT_VALUE = 4;

    private int groupLength = DEFAULT_VALUE;

    public GeneralDataCustomFormatter() {
    }

    public GeneralDataCustomFormatter(int groupLength) {
        this.groupLength = groupLength;
    }

    public String getFormatterName() {
        return "General Data Hex String Formatter";
    }

    public String getFormatterDescription() {
        return "Formats value to hex string with space between specified number of bytes";
    }

    public DataType[] getSupportedTypes() {
        return null;
    }

    public Class[] getSupportedTypesClasses() {
        return new Class[]{TypeGeneralData.class};
    }

    public boolean isTypeSupported(DataType valueType) {
        return valueType != null && valueType.getClass() == TypeGeneralData.class;
    }

    public int[] getSupportedFormats() {
        return new int[]{FORMAT_CUSTOM};
    }

    public boolean isFormatSupported(int format) {
        return format == FORMAT_CUSTOM;
    }

    public String getFormatShortDescription(int format) {
        checkFormat(format);
        return "hex string where groups of N bytes devided by space (each byte - 2 hex letters)";
    }

    public String getFormatLongDescription(int format) {
        checkFormat(format);
        return "general data value formatted as string of hexadecimal letters where groups of N*2 letters " +
                "(from right to left) are separated by space (N - number of bytes to form a group, each byte is " +
                "represented by 2 hexadecimal letters)";
    }

    public String[] getSupportedParameters() {
        return new String[]{PARAMETER_GROUP_LENGTH};
    }

    public Class getParameterType(String parameter) {
        checkParameter(parameter);
        return Integer.class;
    }

    public Object getParameterValue(String parameter) {
        checkParameter(parameter);
        return new Integer(groupLength);
    }

    public void setParameterValue(String parameter, Object value) {
        checkParameter(parameter);
        if (value == null) {
            this.groupLength = DEFAULT_VALUE;
        } else if (value instanceof Integer) {
            int iValue = ((Integer) value).intValue();
            if (iValue < 0) {
                throw new IllegalArgumentException("GroupLength parameter value must be non-negative integer");
            }
            this.groupLength = iValue;
        } else {
            throw new IllegalArgumentException("invalud parameter type " + StringUtils.getSimpleName(value.getClass()));
        }
    }

    private void checkFormat(int format) {
        if (!isFormatSupported(format)) {
            throw new IllegalArgumentException("unsupported format");
        }
    }

    private void checkParameter(String parameter) {
        if (!PARAMETER_GROUP_LENGTH.equals(parameter)) {
            throw new IllegalArgumentException("unsupported parameter");
        }
    }

    public CustomFormatter derive(Map params) {
        if (params == null) {
            throw new NullPointerException("params is null");
        }
        GeneralDataCustomFormatter res = new GeneralDataCustomFormatter();
        res.setParameterValue(PARAMETER_GROUP_LENGTH, params.get(PARAMETER_GROUP_LENGTH));
        return res;
    }

    public String format(DataValue value, int format) {
        checkFormat(format);
        if (value == null) {
            throw new NullPointerException("value is null");
        }
        if (!isTypeSupported(value.getType())) {
            throw new IllegalArgumentException("unsupported value type " + value.getType());
        }
        GeneralData gdv = (GeneralData) value;
        int n = 0;
        byte[] gdvValue = gdv.bytesValue();
        int firstGroup = groupLength == 0 ? 0 : gdvValue.length % groupLength;
        StringBuffer sb = new StringBuffer(gdvValue.length);
        for (int i = 0; i < gdvValue.length; i++) {
            if (i > 0 && (i == firstGroup || n >= groupLength)) {
                sb.append(' ');
                n = 0;
            }
            StringUtils.appendAsHex(sb, gdvValue[i]);
            if (i >= firstGroup) { // starting to count after first group done
                n++;
            }
        }
        return sb.toString();
    }
}
